package com.juma.cartridge.rlock;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.SetParams;
import redis.clients.jedis.util.Pool;

import com.juma.cartridge.rlock.exception.LockAlreadyExistsException;
import com.juma.cartridge.rlock.exception.LockException;

/**
 * 基于redis的抢占式锁(非公平)实现
 * @ClassName NonFairLock
 * @Description
 * @Author zhuangguangyou
 * @Date 2019年8月1日 上午10:59:17
 * @Version 1.0
 *
 */
public class NonFairLock extends RLock {

	private Pool<Jedis> jedisPool;
	private String lockKey;
	
	
	public NonFairLock(RedisLockManager manager, LockNode node, Thread holder,String lockKey,Pool<Jedis> pool) {
		super(manager, node, holder);
		this.lockKey = lockKey;
		this.jedisPool = pool;
	}


	@Override
	protected void acquireLock(long retryWaitTimes) {
		while(true) {
			Jedis jedis = null;
			try {
				jedis = jedisPool.getResource();
				
				SetParams param = SetParams.setParams().nx().ex(getLockSeconds());
	            String replyCode = jedis.set(lockKey, getId(), param);
				if("OK".equals(replyCode)) {
					node.setExpireTime(System.currentTimeMillis()+getLockSeconds()*1000);
					//成功获得锁
					break;
				}
				
				//如果retryWaitTimes是一个负数,则不等待锁,直接异常返回
				if(retryWaitTimes<0) {
				    throw new LockAlreadyExistsException(lockKey);
				}
				
				//未获得锁,等待和重试
				if(retryWaitTimes>=10) {
					try {
						Thread.sleep(retryWaitTimes);
					}catch(Exception e){
					    throw new LockException("线程被中断",e);
					}	
				}	
			}finally {
				if(jedis!=null) {
					jedis.close();	
				}
			}
		}
	}

	@Override
	protected void releaseLock() {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			String lockId = jedis.get(lockKey);
			if (lockId!=null && lockId.equals(getId())) {
				jedis.del(lockKey);
			}
		}finally {
			if(jedis!=null) {
				jedis.close();	
			}
		}
	}
	
	
	@Override
	public void lease() {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			String lockId = jedis.get(lockKey);
			if (lockId!=null && lockId.equals(getId())) {
				jedis.expire(lockKey, getLockSeconds());
				node.setExpireTime(System.currentTimeMillis()+getLockSeconds()*1000);
			}
		}finally {
			if (jedis!=null) {
				jedis.close();	
			}
		}
	}
	
}
